package md.frolov.bible.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import md.frolov.bible.Constants;
import md.frolov.bible.editors.BibleEditorInput;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class Module implements IStructure, IViewLabelProvider, IURLProvider{
	private List<Book> books = new ArrayList<Book>();
	private String id; 
	private String name;
	private String description;
	private String shortName;
	private String category;
	private String language;
	private File file;
	private int seqCounter = 0;
	
	private String bookRegex = null;
	private Pattern booksPattern = null;
	private Pattern booksStrongPattern;
	private static String regexSuffix = "[\\s\\.]*(\\d+)?:?(\\d+[,\\-\\d]*)?";
	private static String regexStrongSuffix = "[\\s\\.]*(\\d+):?(\\d+[,\\-\\d]*)?";
	private Map<String,Book> bookNamesMap = new HashMap<String, Book>();
	
	private Module(){};
	
	public Module(String id){
		this.id = id;
	}

	public int getNextSeq(){
		return seqCounter++;
	}

	@Override
	public Object[] getChildren() {
		return books.toArray();
	}

	@Override
	public Object getParent() {
		return Archive.getInstance().getCategoryByID(category).getLanguageById(language);
	}

	@Override
	public boolean hasChildren() {
		return books.size()>0?true:false;
	}

	public void addBook(Book b){
		books.add(b);
		b.setModule(this);
	}
	
	public Book getBookByLocalId(String id){
		for (Iterator it = books.iterator(); it.hasNext();) {
			Book book = (Book) it.next();
			if(id.equals(book.getLocalId()))
				return book; 
		}
		return null;
	}
	
	public Book getBookById(String id){
		for (Iterator it = books.iterator(); it.hasNext();) {
			Book book = (Book) it.next();
			if(id.equals(book.getId()))
				return book; 
		}
		return null;
	}
	
	public IStructure getById(String id){
		String tid = id+".";
		for (Iterator<Book> it = books.iterator(); it.hasNext();) {
			Book book = it.next();
			if(tid.startsWith(book.getId()+".")){
				if(book.getId().equals(id)) return book;
				return book.getChapterById(id);
			}
		}
		return null;
	}
	
	public Book getFirstBook(){
		return books.get(0);
	}
	
	public Book getLastBook(){
		return books.get(books.size()-1);
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getShortName() {
		return shortName;
	}

	public void setShortName(String shortName) {
		this.shortName = shortName;
	}

	public String getCategory() {
		return category;
	}

	public void setCategory(String category) {
		this.category = category;
		if(category == null || "".equals(category)) this.category = "categories.other";
	}

	public String getLanguage() {
		return language;
	}

	public void setLanguage(String language) {
		this.language = language;
		if(language==null || "".equals(language)) this.language = "en";
	}
	
	public String getText(){
		return getName();
	}

	public String getId() {
		return id;
	}

	@Override
	public String getLocalId() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getUrl() {
		return getId()+"://";
	}

	public void setFile(File file) {
		this.file = file;
	}

	public File getFile() {
		return file;
	}
	
	public String getVerse(String id){
		List<String> l = new ArrayList<String>();
		l.add(id);
		return getVerses(l).get(0);
	}
	
	public List<String> getVerses(List<String> ids){
		try{
			SAXParserFactory spf = SAXParserFactory.newInstance();
			SAXParser sp = spf.newSAXParser();
			long time1 = System.currentTimeMillis();
			XMLHandler h = new XMLHandler(ids);
			sp.parse(new InputSource(new InputStreamReader(new FileInputStream(getFile()),"UTF-8")), h);
			System.out.println("Parsing time: "+((System.currentTimeMillis()-time1)/1000.0));
			return h.getResult();
		} catch (FileNotFoundException e){
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return ids;
	}
	
	private class XMLHandler extends DefaultHandler{
		List<String> ret;
		int lastIndex=-1;
		boolean append = false;
		String beginId = null;
		String endId = null;
		StringBuffer sb = new StringBuffer();
		
		public XMLHandler(List<String> ids){
			ret = new ArrayList<String>(ids);
		}
		
		public List<String> getResult(){
			return ret;
		}

		@Override
		public void characters(char[] ch, int start, int length) throws SAXException {
			// TODO Auto-generated method stub
			if(lastIndex>=0)
				sb.append(ch,start,length);
		}

		@Override
		public void endElement(String uri, String localName, String name) throws SAXException {
			if(name.equals(Constants.XML_VERSE_TAG))
				if(lastIndex>=0){
					if(append) return;
					ret.set(lastIndex, sb.toString());
					sb.setLength(0);
					lastIndex = -1;
				}
		}

		@Override
		public void startElement(String uri, String localName, String name,	Attributes attributes) throws SAXException {
			if(name.equals(Constants.XML_VERSE_TAG)){
				if(append){
					if(attributes.getValue(Constants.XML_ATTRIBUTE_ID).equals(endId))
						append = false;
					return;
				}
				
				for(int i = 0;i<ret.size();i++)
					if(ret.get(i).startsWith(attributes.getValue(Constants.XML_ATTRIBUTE_ID))){
						lastIndex = i;
						beginId = ret.get(i);
						if(beginId.contains("-")){
							String[] s = beginId.split("-");
							beginId = s[0];
							if(!attributes.getValue(Constants.XML_ATTRIBUTE_ID).equals(beginId)){
								lastIndex = -1;
								return;
							}
							endId = s[1];
							append=true;
						}
					}
			}
		}
	}
	
	private static final Pattern versePattern = Pattern.compile("bible\\.([a-zA-Z0-9]{3})\\.([0-9]{1,3})\\.?([0-9]{1,3})?(-bible\\.)?([a-zA-Z0-9]{3})?\\.?([0-9]{1,3})?\\.?([0-9]{1,3})?"); //?-bible?([a-zA-Z0-9])?\\.?([0-9]{1,3})?\\.?([0-9]{1,3})? 
	
	public String getFullName(String id){
		Matcher m = versePattern.matcher(id);
	
		if (!m.matches()) return "!unknown!";
		
		Book book = getBookByLocalId(m.group(1));
		StringBuffer sb = new StringBuffer();
		sb.append(book.getName()).append(" ").append(m.group(2));
		if(m.group(3)!=null) sb.append(":").append(m.group(3));
		if(m.group(6)!=null || m.group(7)!=null){
			sb.append("-");
			if(m.group(6)!=null) sb.append(m.group(6));
			if(m.group(6)!=null && m.group(7)!=null) sb.append(":");
			if(m.group(7)!=null) sb.append(m.group(7));
		}
		return sb.toString();
	}

	public String getBooksRegex(boolean strong){
		if(bookRegex==null){
			StringBuilder sb = new StringBuilder();
			sb.append("(");
			Set<String> names = bookNamesMap.keySet();
			for (Iterator<String> it = names.iterator(); it.hasNext();) {
				sb.append(it.next());
				if(it.hasNext())
					sb.append("|");
			}
			sb.append(")");
			if(strong)
				sb.append(regexStrongSuffix);
			else
				sb.append(regexSuffix);
			bookRegex = sb.toString();
		}
		return bookRegex;
	}
	
	public Pattern getRegexPattern(){
		if(booksPattern==null){
			booksPattern = Pattern.compile(getBooksRegex(false));
		}
		return booksPattern;
	}
	
	public Pattern getStrongRegexPattern(){
		if(booksStrongPattern==null){
			booksStrongPattern = Pattern.compile(getBooksRegex(true));
		}
		return booksStrongPattern;
	}
	
	public Book getBookByShortName(String bookShortName){
		return bookNamesMap.get(bookShortName.toLowerCase());
	}
	
	public void addBookShortName(String bookShortName, Book  book){
		String key = bookShortName.toLowerCase().replaceAll("[\\(\\)\\$\\\\\\^\\[\\]\\+\\*\\?]+", "");
		bookNamesMap.put(key, book);
	}
	
	public boolean matches(String yok){
		return getRegexPattern().matcher(yok.toLowerCase()).matches();
	}
	
	public String getUrlForStringLocation(String yok){
		Matcher m = getRegexPattern().matcher(yok.toLowerCase());
		if(!m.matches()) return null;
		
		
		return getUrlForStringLocation(m.group(1), m.group(2), m.group(3));
	}
	
	public String getUrlForStringLocation(String bookName, String chapterId, String verseId){
		Book book = getBookByShortName(bookName);
		if(book==null) return null;
		
		StringBuilder url = new StringBuilder();
		url.append(book.getUrl());
		if(chapterId!=null){
			url.append('.').append(chapterId);
			if(verseId!=null)
				url.append('.').append(verseId);
		}
		
		return url.toString();
	}

	public List<Book> getBooks() {
		return books;
	}
}
